package postpc.powerreminders.com.location;

import postpc.powerreminders.com.db.RotgDatabase;
import postpc.powerreminders.com.location.RotgLocationManager.LocationManagerCallback;
import android.content.Context;
import android.database.Cursor;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.widget.Toast;

/**
 * This class implements LocationListener, which listens for both
 * changes in the location of the device and changes in the status
 * of the GPS system.
 */
public class GPSController implements LocationListener {

	/*
	 * Logger tag
	 */
	static final String TAG = "ROTG_GPS_LOCATION";

	/*
	 * Data constants
	 */
	private static final float GPS_ALERT_DIST = 30;
	private static final long GPS_MAX_WATCHDOG_TIMEOUT = 300000; // 5 Min

	/*
	 * Reference to the location manager for GPS services.
	 */
	LocationManager locationManager;

	/*
	 * Reference to the ROTG location manager.
	 */
	RotgLocationManager rotgLocationm;

	/*
	 * Reference to the power manager.
	 */
	RotgPowerManager powerManager;

	/*
	 * Reference to the dataBase.
	 */
	RotgDatabase dataBase;
	
	/*
	 * Watch dog for GPS turn off.
	 */
	Handler watchdog = new Handler();
	
	/*
	 * Delayed task handler.
	 */
	Handler handler = new Handler();
	
	/*
	 * Variables for returning
	 */
	private boolean returnResults = false;;
	private LocationManagerCallback callback = null;

	/**	
	 * GPS constructor
	 * 
	 * @param mainActivity
	 */
	public GPSController (RotgLocationManager lm,
						RotgPowerManager pm, 
						RotgDatabase db){

		Log.i(TAG, "Initializing ROTG GPS");
		this.rotgLocationm = lm;
		this.locationManager = (LocationManager)this.rotgLocationm.context.getSystemService(Context.LOCATION_SERVICE);
		this.powerManager = pm;
		this.dataBase = db;

		// Start listening for GPS coordinates
		requestGPSLocation();
	}

	/**
	 * On location changed listener
	 */
	@Override
	public void onLocationChanged(Location mylocation) {
		
		// Pet the watchdog
		this.watchdog.removeCallbacks(watchdogTimeTask);
				
		Log.i(TAG, "GPS Location Changed");
		Log.i(TAG, "Londitude: " + mylocation.getLongitude());
		Log.i(TAG, "Latitude: " + mylocation.getLatitude());
		Log.i(TAG, "Altitiude: " + mylocation.getAltitude());
		Log.i(TAG, "Accuracy: " + mylocation.getAccuracy());
		Log.i(TAG, "Timestamp: " + mylocation.getTime());
		
		// Go over the database and find if we need to alert.
		Cursor cursor = this.dataBase.fetchAll_GPS_Reminders();
		if (cursor != null && cursor.moveToFirst() && cursor.getCount() !=0) {
			do {
				Log.i(TAG, "REMINDER #" + cursor.getString(0));

				// Create location to calculate distance to.
				Location databaseLocation = new Location("GPS");
				databaseLocation.setLongitude(cursor.getDouble(cursor.getColumnIndexOrThrow(RotgDatabase.GPS_LONGITUDE)));
				databaseLocation.setLatitude(cursor.getDouble((cursor.getColumnIndexOrThrow(RotgDatabase.GPS_LATITUDE))));
				if (mylocation.distanceTo(databaseLocation) < GPS_ALERT_DIST){
					// ALERT
					this.rotgLocationm.notifyEventGPS(cursor);
					this.dataBase.deactivateReminder(cursor.getInt(0));
				}		       
			} while (cursor.moveToNext());
		}
		if (cursor != null && !cursor.isClosed() && cursor.getCount() !=0) {
			cursor.close();
		}
		
		// Power manager evaluation
		this.powerManager.evaluateGPS(mylocation);
		removeGPSUpdates();
		
		// User requested location
		if (this.returnResults){
			Log.i("IMPORTANT", "Returning BSSID = " + mylocation.getLongitude() + "SSID = " + mylocation.getLatitude());
			this.callback.gpsInfoAvailable(mylocation.getLongitude(), mylocation.getLatitude());
			this.returnResults = false;
			this.callback = null;
		}
		
		if (this.powerManager.GPSStayOnStatus() == true){
			Log.i(TAG, "Setting GPS to start again");
			requestGPSLocation();
		}
	}

	/**
	 * Request GPS location service.
	 */
	public void requestGPSLocation() {
		Log.i(TAG, "Requesting GPS Location");
		watchdog.removeCallbacks(watchdogTimeTask);
		watchdog.postDelayed(watchdogTimeTask, GPS_MAX_WATCHDOG_TIMEOUT);
		locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 
				powerManager.getMinTime(),
				powerManager.getMinDistance(), 
				this);
	}

	/**
	 * Clear GPS location service request.
	 */
	public void removeGPSUpdates() {
		Log.i(TAG, "Removed GPS updates");
		locationManager.removeUpdates(this);
	}

	/**
	 *  this is called if/when the GPS is disabled in settings 
	 */
	@Override
	public void onProviderDisabled(String provider) {	
		Log.w(TAG, "GPS Disabled");
		this.removeGPSUpdates();
		
		/* bring up the GPS settings 
		Intent intent = new Intent(
				android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
		this.rotgLocationm.context.startActivity(intent);
		this.rotgLocationm.notifyUserDialog("GPS Disabled, Please re-enable the GPS");
		*/
	}

	/**
	 *  This is called if/when the GPS is enabled in settings 
	 */
	@Override
	public void onProviderEnabled(String provider) {
		Log.i(TAG, "GPS Enabled");
		this.requestGPSLocation();
	}

	/** 
	 * This is called when the GPS status alters 
	 */
	@Override
	public void onStatusChanged(String provider, int status, Bundle extras) {
		Log.i(TAG, "Status Changed to " + status);
		switch (status) {
		case LocationProvider.OUT_OF_SERVICE:
			Log.w(TAG, "Status Changed: Out of Service");
			Toast.makeText(this.rotgLocationm.context, "Status Changed: Out of Service",
					Toast.LENGTH_SHORT).show();
			break;

		case LocationProvider.TEMPORARILY_UNAVAILABLE:
			Log.v(TAG, "Status Changed: Temporarily Unavailable");
			Toast.makeText(this.rotgLocationm.context, "Status Changed: Temporarily Unavailable",
					Toast.LENGTH_SHORT).show();
			break;

		case LocationProvider.AVAILABLE:
			Log.v(TAG, "Status Changed: Available");
			Toast.makeText(this.rotgLocationm.context, "Status Changed: Available",
					Toast.LENGTH_SHORT).show();
			break;
		}
	}
	
	/**
	 * Task Handler for the watch dog 
	 */
	public Runnable watchdogTimeTask = new Runnable() {
		public void run() {
			Log.i(TAG, "Watchdog Timer Called");
			//rotgLocationm.notifyUserDialog("GPS cannot get a signal, Going to sleep");
			removeGPSUpdates();
		}
	};
	
	/**
	 * Task Handler for the GPS 
	 */
	public Runnable updateTimeTask = new Runnable() {
		public void run() {
			Log.i(TAG, "GPS Timer Called");
			requestGPSLocation();
		}
	};

	/**
	 * User requested location.
	 * 
	 * @param callback
	 */
	public void requestCurrentLocation(LocationManagerCallback callback) {
		this.returnResults = true;
		this.callback = callback;
		this.requestGPSLocation();
	}
}